/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <isa.h>
#include <cpu/cpu.h>
#include <difftest-def.h>
#include <memory/paddr.h>
#include <CSRs.h>

void init_rand();
void init_mem();
void init_csrs();
void difftest_memcpy(paddr_t addr, void *buf, size_t n, bool direction) {
	if(direction == DIFFTEST_TO_REF){
		memcpy(guest_to_host(addr),buf,n);
	}
	else assert(0);
}

uint64_t last_pc;
uint64_t difftest_get_npc(uint64_t pc);
void difftest_regcpy_last_toref(void *dut){
		memcpy(&cpu,dut,DIFFTEST_REG_SIZE);
		//printf("pc_last1= %lx\n",cpu.pc);
}
void difftest_csrscpy(uint64_t* start){
		write_csr(0x342,*start);		//cause
		write_csr(0x300,*(start+1));		//status
		write_csr(0x341,*(start+2));//mepc
}
int intr_happened;
void difftest_intrcpy(){
		intr_happened = 1;
}
void difftest_regcpy(void *dut, bool direction) {
	if(direction ==DIFFTEST_TO_REF){
		if(intr_happened == 0){
		memcpy(&last_pc,dut+DIFFTEST_REG_SIZE-sizeof(uint64_t),sizeof(uint64_t));
		//printf("pc_last = %lx\n",last_pc);
		cpu.pc =  difftest_get_npc(last_pc);
		//printf("pc_now= %lx\n",cpu.pc);
		}
		else {cpu.pc = read_csr(0x305);}
		intr_happened = 0;
		memcpy(&cpu,dut,DIFFTEST_REG_SIZE-sizeof(uint64_t));
	}
	else if(direction ==DIFFTEST_TO_DUT){
		memcpy(dut,&cpu,DIFFTEST_REG_SIZE-sizeof(uint64_t));	
		memcpy(dut+DIFFTEST_REG_SIZE-sizeof(uint64_t),(void*)&last_pc,sizeof(uint64_t));
	}
	else{assert(0);}
}
void difftest_exec(uint64_t n) {
	cpu_exec(n);
  //assert(0);
}

void difftest_raise_intr(word_t NO) {
  assert(0);
}

void difftest_init(int port) {
  /* Perform ISA dependent initialization. */
	init_rand();
	init_mem();
	for(int i=0;i<32;i++)
	{
					cpu.gpr[i] = 0;
	}
	init_csrs();
	last_pc = 0x80000000;
	cpu.pc = 0x80000000;
 // init_isa();
}
